//
// NADA Mobile IOPack firmware for Arduino
//
// Created by Brian Hinch in March. 2009
// Last update October 2009
//
// Version 0001
//
//

///////////////////////////////////
//
// constants
//

//for USB arduino:
//const int SERIAL_BAUD_RATE                    = 9600; //Baud rate for serial communication between Arduino and computer (try 9600 if others don't work)

//for BT arduino:
const int SERIAL_BAUD_RATE                    = 9600; //Baud rate for serial communication between Arduino and computer (try 9600 if others don't work)


const int MESSAGE_BYTES_PER_VALUE             = 2;
const int SET_MESSAGE_NUN_VALUES              = 10;
const int SET_MESSAGE_LENGTH                  = 1 + (SET_MESSAGE_NUN_VALUES * MESSAGE_BYTES_PER_VALUE);
const int GET_MESSAGE_LENGTH                  = 1;
const int LONGEST_POSSIBLE_MESSAGE_LENGTH     = SET_MESSAGE_LENGTH; //no message length may be greater than this value

const int MESSAGE_OFFSET_OPCODE               = 0;

const byte GET_MESSAGE_OPCODE                 = 0x81;         // 1000 0001
const byte SET_MESSAGE_OPCODE                 = 0x83;         // 1000 0011

const byte MESSAGE_OPCODE_MASK                = 0x01;         // 1000 0000
const byte MESSAGE_VALUE_MASK                 = 0x0F;         // 0000 1111

const int OUTGOING_SET_MESSAGE_RESEND_COUNT   = 10;
const int OUTGOING_SET_MESSAGE_RESEND_DELAY   = 100;

const int GET_RESPONSE_TIMEOUT_MS             = 25;

const int OUTPUT_PINS[] =          {3, 5, 6, 9, 10, 11, -1, -1, -1, -1};       //these are the pin #'s that map to each output value in an incoming SET MESSAGE, use -1 to indicate that this board doesn't have a particular pin
const int INPUT_PINS[] =           {0, 1, 2, 3, 4,  5,  -1, -1, -1, -1};        //these are the pin #'s that map to each input value in an outgoing SET MESSAGE, use -1 to indicate that this board doesn't have a particular pin

int LAST_INPUT_VALUES[] =          {0, 0, 0, 0, 0,  0,   0,  0,  0,  0};

//const int LED_PIN = 13;

///////////////////////////////////
//
// global variables
//

int incomingByte = 0; // for incoming serial data

byte incomingMessageBuffer[LONGEST_POSSIBLE_MESSAGE_LENGTH];                 //stores the bytes of an incoming message
int incomingMessageBufferCursor = 0;
int incomingMessageBufferExpectedLength = 0;
byte outgoingSetMessageBuffer[SET_MESSAGE_LENGTH];

int value;
int reading;
int bufferOffset;
int i;
byte b;
boolean ainchanged;


///////////////////////////////////
//
// logic
//

void setup() {
  for (i = 0; i < SET_MESSAGE_NUN_VALUES; i++) {
    if (OUTPUT_PINS[i] > 0) {
      pinMode(OUTPUT_PINS[i], OUTPUT);
      analogWrite(OUTPUT_PINS[i], 0);
    }
  }
  Serial.begin(SERIAL_BAUD_RATE);	// opens serial port, sets data rate to 9600 bps
}

void detectChangedAnalogInputs() {
  //check the analog inputs for changes
  ainchanged = false;
  for (i = 0; ((i < SET_MESSAGE_NUN_VALUES) && !ainchanged); i++) {
    if (INPUT_PINS[i] >= 0) {
      //the server is expecting a number between 0-255 (1 byte), but arduino provides a 0-1023 value (2-byte)
      reading = analogRead(INPUT_PINS[i]);
      if (reading != LAST_INPUT_VALUES[i]) ainchanged = true;
    }
  }  //for each logical value expected in a SET MESSAGE
  if (ainchanged) sendSetMessage();
} //detectChangedAnalogInputs

void loop() {
  // send data only when you receive data:
  if (Serial.available() > 0) {
    incomingByte = Serial.read();
    
    //check for opcodes
    
    if (incomingByte == GET_MESSAGE_OPCODE) {
      resetIncomingMessageBuffer();
      incomingMessageBufferExpectedLength = GET_MESSAGE_LENGTH;
    } else if (incomingByte == SET_MESSAGE_OPCODE) {
      resetIncomingMessageBuffer();
      incomingMessageBufferExpectedLength = SET_MESSAGE_LENGTH;
    }
    
    //store this byte in the buffer
    incomingMessageBuffer[incomingMessageBufferCursor] = incomingByte;
    incomingMessageBufferCursor++;
    
    if (incomingMessageBufferCursor >= incomingMessageBufferExpectedLength) {
      //we now have the expected number of bytes in the buffer, so parse the buffer contents
      parseIncomingMessageBuffer();
      resetIncomingMessageBuffer();
      
    } else if (incomingMessageBufferCursor >= LONGEST_POSSIBLE_MESSAGE_LENGTH) {
      //this shouldn't happen, but to prevent a buffer overrun reset everything if we get LONGEST_POSSIBLE_MESSAGE_LENGTH bytes in the buffer before the end of a message
      resetIncomingMessageBuffer();
    }
    
  } //end if serial data available
}

//
// prepare the message buffer to receive a new message
//
void resetIncomingMessageBuffer() {
  for (i = 0; i < LONGEST_POSSIBLE_MESSAGE_LENGTH; i++) incomingMessageBuffer[i] = 0; //zero the message buffer (might not be necessary)
  incomingMessageBufferCursor = 0;
} //resetIncomingMessageBuffer

//
// take action based on the current contents of the message buffer
//
void parseIncomingMessageBuffer() {
  if (incomingMessageBuffer[0] == GET_MESSAGE_OPCODE) {
    // received a GET MESSAGE, so respond by sending a SET MESSAGE
    sendSetMessage();
  } else if (incomingMessageBuffer[0] = SET_MESSAGE_OPCODE) {
    // received a SET MESSAGE, so look at the remaining bytes to get the values to which outputs should be set
    
    //debug - flash the LED each time a set message comes in
    digitalWrite(13, HIGH);
    delay(500);
    digitalWrite(13, LOW);
    
    //convert encoded values
    
    //int values[SET_MESSAGE_NUN_VALUES]; //this will hold the logical values encoded in the SET MESSAGE
    for (i = 0; i < SET_MESSAGE_NUN_VALUES; i++) {
      bufferOffset = 1 + (i * MESSAGE_BYTES_PER_VALUE);
      value = ((incomingMessageBuffer[bufferOffset] & MESSAGE_VALUE_MASK) << 4)
              +
              (incomingMessageBuffer[bufferOffset + 1] & MESSAGE_VALUE_MASK);
      if (OUTPUT_PINS[i] >= 0) {
        if (value > 0) {
          digitalWrite(OUTPUT_PINS[i], HIGH);
          digitalWrite(13, HIGH);
        } else if (value <= 0) {
          digitalWrite(OUTPUT_PINS[i], LOW);
          digitalWrite(13, LOW);
        } else {
          analogWrite(OUTPUT_PINS[i],((min(value, 255) + 1) * 4)-1); //analogWrite expects to never receive a number > 1023
          digitalWrite(13, LOW);
        }
      }
    } //for each logical value expected in a SET MESSAGE
    
  }
  
} //parseIncomingMessageBuffer

//
// send a SET MESSAGE that contains the current state of the board's analog inputs
//
void sendSetMessage() {
  
  //debug - flash the LED each time a set message comes in
  /*
  digitalWrite(13, HIGH);
  delay(500);
  digitalWrite(13, LOW);
  delay(1000);
  */
  
  Serial.print(SET_MESSAGE_OPCODE, BYTE);
  for (i = 0; i < SET_MESSAGE_NUN_VALUES; i++) {
    if (INPUT_PINS[i] >= 0) {
      //the server is expecting a number between 0-255 (1 byte), but arduino provides a 0-1023 value (2-byte)
      LAST_INPUT_VALUES[i] = analogRead(INPUT_PINS[i]);
      value = (LAST_INPUT_VALUES[i] << 2) >> 4;
    } else {
      LAST_INPUT_VALUES[i] = 0;
      value = 0;
    }
    //write the first encoded byte for this value
    b = (value >> 4) & 0x0F;
    Serial.print(b, BYTE);
    //write the second encoded byte for this value
    b = value & 0x0F;
    Serial.print(b, BYTE);
  }  //for each logical value expected in a SET MESSAGE
  
} //sendSetMessage

